//接口
package channel

import (
	"fmt"
	"time"
)

//创建channel
//定义一个channel时，也需要定义发送到channel的值的类型。注意，必须使用make 创建channel
func test1() {
	ci := make(chan int)
	cs := make(chan string)
	cf := make(chan interface{})
	fmt.Println(ci)
	fmt.Println(cs)
	fmt.Println(cf)
}

//channel通过操作符<-来接收和发送数据
//默认情况下，channel接收和发送数据都是阻塞的，
//除非另一端已经准备好，这样就使得Goroutines同步变的更加的简单，
//而不需要显式的lock。所谓阻塞，也就是如果读取（value := <-ch）它将会被阻塞，
//直到有数据接收。其次，任何发送（ch<-5）将会被阻塞，直到数据被读出。
//无缓冲channel是在多个goroutine之间同步很棒的工具。
func test2() {
	ch := make(chan string, 4) //创建了可以存储4个元素的string 型channel
	send := "hello"
	ch <- send  // 发送v到channel ch.
	get := <-ch // 从ch中接收数据，并赋值给v
	fmt.Println(get)
}

//汇总并将结果发送到channel
func sum(a []int, c chan int) {
	total := 0
	for _, v := range a {
		total += v
	}
	c <- total // send total to c
}

//数组分段汇总
func test3() {
	a := []int{7, 2, 8, -9, 4, 0}
	c := make(chan int)
	go sum(a[:len(a)/2], c)
	go sum(a[len(a)/2:], c)
	x, y := <-c, <-c // receive from c
	fmt.Println(x, y, x+y)
}

func fibonacci(n int, c chan int) {
	x, y := 1, 1
	for i := 0; i < n; i++ {
		c <- x
		x, y = y, x+y
	}
	close(c)
}

//迭代和关闭缓存型channel
func test4() {
	c := make(chan int, 10)
	go fibonacci(cap(c), c)
	for i := range c {
		fmt.Println(i)
	}
}

func fibonacci5(c, quit chan int) {
	x, y := 1, 1
	for {
		select {
		case c <- x:
			x, y = y, x+y
		case <-quit:
			fmt.Println("quit")
			return
		}
	}
}

//通过select可以监听channel上的数据流动。
//select默认是阻塞的，只有当监听的channel中有发送或接收可以进行时才会运行，
//当多个channel都准备好的时候，select是随机的选择一个执行的。
func test5() {
	c := make(chan int)
	quit := make(chan int)
	go func() {
		for i := 0; i < 10; i++ {
			fmt.Println(<-c)
		}
		quit <- 0
	}()
	fibonacci5(c, quit)
}

//超时
func test6() {
	c := make(chan int)
	o := make(chan bool)
	go func() {
		for {
			select {
			case v := <-c:
				println(v)
			case <-time.After(5 * time.Second):
				println("timeout")
				o <- true
				break
			}
		}
	}()
	<-o
}

func TestChannel() {
	test1()
	test2()
	test3()
}
